package com.luhuiguo.cms.server.config;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.Filter;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory;
import org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice;
import org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PostInvocationAdviceProvider;
import org.springframework.security.access.prepost.PostInvocationAuthorizationAdvice;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.BeanIds;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;

import com.luhuiguo.cms.server.service.UserService;

/**
 * The spring java-based configuration for spring security.
 * 
 * @author LuHuiguo
 * @version $Id: SecurityConfig.java 158 2010-05-07 03:05:07Z luhuiguo@gmail.com
 *          $
 */
@Configuration
public class SecurityConfig {

    @Inject
    private UserService userService;

    @Bean
    public PreInvocationAuthorizationAdviceVoter preInvocationVoter() {
        return new PreInvocationAuthorizationAdviceVoter(preAuthorizationAdvice());
    }

    @Bean
    public PostInvocationAdviceProvider afterInvocation() {
        return new PostInvocationAdviceProvider(postAuthorizationAdvice());
    }

    @Bean
    public PrePostAnnotationSecurityMetadataSource prePostAnnotationSecurityMetadataSource() {
        return new PrePostAnnotationSecurityMetadataSource(annotationInvocationFactory());
    }

    @Bean
    public MethodSecurityExpressionHandler expressionHandler() {
        return new DefaultMethodSecurityExpressionHandler();
    }

    @Bean
    public PreInvocationAuthorizationAdvice preAuthorizationAdvice() {
        ExpressionBasedPreInvocationAdvice preInvocationAuthorizationAdvice = new ExpressionBasedPreInvocationAdvice();
        preInvocationAuthorizationAdvice.setExpressionHandler(expressionHandler());
        return preInvocationAuthorizationAdvice;
    }

    @Bean
    public PostInvocationAuthorizationAdvice postAuthorizationAdvice() {
        return new ExpressionBasedPostInvocationAdvice(expressionHandler());
    }

    @Bean
    public PrePostInvocationAttributeFactory annotationInvocationFactory() {
        return new ExpressionBasedAnnotationAttributeFactory(expressionHandler());
    }

    @Bean
    public RoleVoter roleVoter() {
        return new RoleVoter();
    }

    @Bean
    public AuthenticatedVoter authenticatedVoter() {
        return new AuthenticatedVoter();
    }

    @Bean
    public AffirmativeBased affirmativeBased() {
        AffirmativeBased affirmativeBased = new AffirmativeBased();

        List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();

        voters.add(preInvocationVoter());
        voters.add(roleVoter());
        voters.add(authenticatedVoter());

        affirmativeBased.setDecisionVoters(voters);

        return affirmativeBased;
    }

    @Bean
    public DelegatingMethodSecurityMetadataSource delegatingMethodSecurityMetadataSource() {
        DelegatingMethodSecurityMetadataSource delegatingMethodSecurityMetadataSource = new DelegatingMethodSecurityMetadataSource();
        List<MethodSecurityMetadataSource> delegates = new ArrayList<MethodSecurityMetadataSource>();
        delegates.add(prePostAnnotationSecurityMetadataSource());
        delegatingMethodSecurityMetadataSource.setMethodSecurityMetadataSources(delegates);
        return delegatingMethodSecurityMetadataSource;
    }

    @Bean
    public AuthenticationProvider authProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userService);
        return authProvider;
    }

    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManager() {
        ProviderManager providerManager = new ProviderManager();
        List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>();
        providers.add(authProvider());
        providerManager.setProviders(providers);
        return providerManager;
    }

    @Bean
    public UrlMatcher urlMatcher() {
        return new AntUrlPathMatcher();
    }
    
    @Bean
    public PortMapper portMapper() {
        return new PortMapperImpl();
    }

    @Bean(name = { BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN })
    public FilterChainProxy filterChainProxy() {
        FilterChainProxy filterChainProxy = new FilterChainProxy();

        filterChainProxy.setMatcher(urlMatcher());
        filterChainProxy.setStripQueryStringFromUrls(true);

        Map<String, List<Filter>> filterChainMap = new HashMap<String, List<Filter>>();
        
       // filterChainMap.put(key, value)
        
        filterChainProxy.setFilterChainMap(filterChainMap);
        
        return filterChainProxy;
    }
    

    
    @Bean
    public RememberMeServices rememberMeServices() {
        return new TokenBasedRememberMeServices();
    }

    @Bean
    public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
        return new RememberMeAuthenticationProvider();
    }   
    
    @Bean
    public FilterSecurityInterceptor filterSecurityInterceptor() {
        return new FilterSecurityInterceptor();
    }
    
}
